﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media.Animation;

namespace SilverFlow.Controls
{
    /// <summary>
    /// Taskbar containing minimized windows.
    /// </summary>
    [TemplatePart(Name = PART_OpenButton, Type = typeof(ButtonBase))]
    [TemplatePart(Name = PART_Bar, Type = typeof(Border))]
    [TemplatePart(Name = PART_SlidingBar, Type = typeof(Border))]
    [TemplatePart(Name = PART_Carousel, Type = typeof(StackPanel))]
    public partial class Taskbar : ContentControl
    {
        // Template parts
        private const string PART_OpenButton = "OpenButton";
        private const string PART_Bar = "Bar";
        private const string PART_SlidingBar = "SlidingBar";
        private const string PART_Carousel = "Carousel";

        // VSM states
        private const string VSMSTATE_StateNormal = "Normal";

        // Animation duration in milliseconds
        private const double OpeningDurationInMilliseconds = 300;
        private const double ClosingDurationInMilliseconds = 200;
        private const double BootstrapMovementDurationInMilliseconds = 200;

        private bool isOpen;
        private ButtonBase openButton;
        private Border bar;
        private Border slidingBar;
        private StackPanel carousel;
        private bool bootstrapButtonIsVisible;

        /// <summary>
        /// Gets the FloatingWindowHost containing the Taskbar.
        /// </summary>
        /// <value>FloatingWindowHost containing the Taskbar.</value>
        private FloatingWindowHost Host
        {
            get { return this.Parent as FloatingWindowHost; }
        }

        /// <summary>
        /// Gets the height of the bootstrap button depending on its visibility.
        /// </summary>
        /// <value>The height of the bootstrap button.</value>
        private double BootstrapButtonHeight
        {
            get
            {
                return bootstrapButtonIsVisible ? openButton.Height : 0;
            }
        }

        /// <summary>
        /// Shows the bootstrap button if it is hidden.
        /// </summary>
        public void ShowBootstrapButton()
        {
            if (!bootstrapButtonIsVisible)
            {
                bootstrapButtonIsVisible = true;
                UpdateBootstrapButtonPosition();
            }
        }

        /// <summary>
        /// Hides the bootstrap button if it is visible.
        /// </summary>
        public void HideBootstrapButton()
        {
            if (bootstrapButtonIsVisible)
            {
                bootstrapButtonIsVisible = false;
                UpdateBootstrapButtonPosition();
            }
        }

        /// <summary>
        /// Builds the visual tree for the <see cref="Taskbar" /> control 
        /// when a new template is applied.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            openButton = GetTemplateChild(PART_OpenButton) as ButtonBase;
            bar = GetTemplateChild(PART_Bar) as Border;
            slidingBar = GetTemplateChild(PART_SlidingBar) as Border;
            carousel = GetTemplateChild(PART_Carousel) as StackPanel;

            if (openButton == null)
                throw new NotImplementedException("Template Part PART_OpenButton is required to display Taskbar.");

            if (bar == null)
                throw new NotImplementedException("Template Part PART_Bar is required to display Taskbar.");

            if (slidingBar == null)
                throw new NotImplementedException("Template Part PART_SlidingBar is required to display Taskbar.");

            if (carousel == null)
                throw new NotImplementedException("Template Part PART_Carousel is required to display Taskbar.");

            if (Host != null)
                Host.SizeChanged += new SizeChangedEventHandler(Host_SizeChanged);

            bar.MouseLeave += new System.Windows.Input.MouseEventHandler(Bar_MouseLeave);
            bar.MouseMove += new MouseEventHandler(Bar_MouseMove);

            openButton.Click += new RoutedEventHandler(OpenButton_Click);
        }

        /// <summary>
        /// Shifts the taskbar up or down, i.e. makes the bootstrap button visible or not.
        /// </summary>
        private void UpdateBootstrapButtonPosition()
        {
            if (!isOpen)
            {
                double y = Host.ActualHeight - BootstrapButtonHeight + 1;
                this.MoveCanvasTop(y, BootstrapMovementDurationInMilliseconds);
            }
        }

        /// <summary>
        /// Updates position of the Taskbar on host's SizeChanged event.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.SizeChangedEventArgs"/> instance containing the event data.</param>
        private void Host_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Width = Host.ActualWidth;
            double y = isOpen ? (Host.ActualHeight - this.ActualHeight) : (Host.ActualHeight - BootstrapButtonHeight + 1);
            Canvas.SetTop(this, y);
        }

        /// <summary>
        /// Handles the Click event of the OpenButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void OpenButton_Click(object sender, RoutedEventArgs e)
        {
            if (!isOpen)
            {
                Canvas.SetLeft(slidingBar, 0);
                FillCarousel();
                OpenTaskbar();
            }
        }

        /// <summary>
        /// Starts taskbar opening animation.
        /// </summary>
        private void OpenTaskbar()
        {
            double y = Host.ActualHeight - bar.ActualHeight;
            var easingFunction = new QuarticEase
                {
                    EasingMode = EasingMode.EaseOut
                };

            this.MoveCanvasTop(y, OpeningDurationInMilliseconds, easingFunction);
            openButton.SetVisible(false);
            isOpen = true;
        }

        /// <summary>
        /// Starts taskbar closing animation.
        /// </summary>
        private void CloseTaskbar()
        {
            double y = Host.ActualHeight - BootstrapButtonHeight + 1;
            this.MoveCanvasTop(y, ClosingDurationInMilliseconds);

            VisualStateManager.GoToState(openButton, VSMSTATE_StateNormal, true);
            carousel.Children.Clear();
            openButton.SetVisible(true);
            isOpen = false;
        }

        /// <summary>
        /// Handles the MouseLeave event of the Bar control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void Bar_MouseLeave(object sender, MouseEventArgs e)
        {
            if (isOpen)
            {
                CloseTaskbar();
            }
        }

        /// <summary>
        /// Handles the MouseMove event of the Bar control. It implements "Carousel" logic.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void Bar_MouseMove(object sender, MouseEventArgs e)
        {
            WindowIcon icon = (from item in carousel.Children.OfType<WindowIcon>()
                               where item.IsMouseOver || item.IsPressed
                               select item).FirstOrDefault();

            // If there is at least one icon on the bar
            if (icon != null)
            {
                double a = e.GetPosition(bar).X;
                double b = bar.ActualWidth - bar.Padding.Horizontal();
                double c = slidingBar.ActualWidth;

                // If sliding bar does not fit into the bar
                if (c > b)
                {
                    double width = b - icon.ActualWidth;
                    if (width != 0)
                    {
                        a -= icon.ActualWidth / 2;
                        if (a < 0) a = 0;
                        if (a > width) a = width;

                        double x = (a / width) * (b - c);
                        Canvas.SetLeft(slidingBar, x);
                    }
                }
            }
        }

        /// <summary>
        /// Add windows icons to the carousel.
        /// </summary>
        private void FillCarousel()
        {
            carousel.Children.Clear();

            var windows = from window in Host.FloatingWindows
                          where window.IsOpen && window.ShowInTaskbar &&
                          !(Host.ShowMinimizedOnlyInTaskbar && window.WindowState != WindowState.Minimized)
                          orderby window.IconText
                          select window;

            foreach (var window in windows)
            {
                WindowIcon icon = new WindowIcon()
                {
                    Title = window.IconText,
                    Thumbnail = window.WindowThumbnail,
                    Window = window
                };

                icon.Click += new RoutedEventHandler(Icon_Click);
                carousel.Children.Add(icon);
            }
        }

        /// <summary>
        /// Handles the Click event of the Icon control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Icon_Click(object sender, RoutedEventArgs e)
        {
            WindowIcon icon = sender as WindowIcon;
            if (icon != null && icon.Window != null)
            {
                icon.Window.RestoreWindow();
                carousel.Children.Clear();
                CloseTaskbar();
            }
        }
    }
}